#ifndef CS148_SUBDIV_H_
#define CS148_SUBDIV_H_

#include "cs148.h"
#include "st.h"
#include <vector>

class Edge;

// minimal Wing Edge structure element
struct Vertex 
{
	STVector3 pos;
};

class Face 
{
	std::vector<Edge*> edges;
	STVector3 fp;	//face point - average of all points on the face
public:
	Face(){}
	~Face(){edges.clear();}
	void addEdge(Edge* edge){edges.push_back(edge);}
	void computeFacePoint();	// this is ugly
	int getNumEdge(){return edges.size();}
	STVector3 getFacePoint(){return fp;}
	bool hasVertex(Vertex* v);
	Edge* preEdge(int i);	// previous edge 
	Edge* getEdge(int i){return edges[i];}
};

// this is not bidirectional edge, no previous and next edge info.
class Edge 
{
	Face* lFace;
	Face *rFace;
	Vertex* vs;
	Vertex* ve;
	STVector3 ep; // edge point - avg. of two vertices and two sides face point
public:
	Edge(Vertex *s, Vertex *e){vs = s; ve = e; lFace = rFace = NULL;}
	void setRFace(Face* face){rFace = face;}
	void setLFace(Face* face){lFace = face;}
	Vertex* startV(Face* face);		// !!! has to check face
	Vertex* endV(Face* face);		// same
	bool reverseEqual(STVector3 s, STVector3 e);
	void computeEdgePoint();
	STVector3 getEdgePoint(){return ep;}
	Face* getLFace(){return lFace;}
	Face* getRFace(){return rFace;}
};

class CatClarkSurface {
public:
	// Load your surface from an OBJ input file.
	CatClarkSurface(const char* filename);

	// Subdivide the surface several times
	void Subdivide(int iterations);

	// Draw the surface at the current level of subdivision
	void Draw() const;

	// Save out the subdivided surface
	void SaveOBJ(const char* filename) const;

	// Get bounding box of surface
	void GetBounds(STVector3& boundMin, STVector3& boundMax) const;
private:
	// Run your subdivision pipeline once
	void SubdivideOnce();

	// Build any mesh data structures you need to
	void BuildMesh();

	// Compute the centroid of all faces in the mesh
	void ComputeFacePoints();

	// Compute the Catmull Clark edge points for each edge (the average
	// of the two edge endpoints and the two adjacent face points).
	void ComputeEdgePoints();

	// Compute the new position of each original vertex. This value is
	// an averaging of the face points, the edge points and the original
	// vertex.
	void ComputeVertexPoints();

	// Now that all the new positions are computed, compute the
	// corresponding topology.
	void ConnectVertices();

	// Sentinel for unimplemented const STVector& functions.
	static const STVector3 kUnimplementedVector;
	// Return the number of faces in the mesh
	unsigned int NumFaces() const;
	// Return the number of vertices in the mesh
	unsigned int NumVertices() const;
	// Get the number of vertices for a given face
	unsigned int NumVerticesInFace(unsigned int face) const;
	// Get the index of the vth vertex of the given face
	unsigned int GetVertexIndex(unsigned int face, unsigned int v) const;
	// Get the vertex data for vertex i
	const STVector3& GetVertex(unsigned int i) const;
	// Get the vertex data for vertex v of the given face.
	const STVector3& GetVertexOfFace(unsigned int face, unsigned int v) const;

	// Raw input data
	std::vector<STVector3> raw_vertices;
	std::vector<unsigned int> raw_indices;
	std::vector<unsigned int> raw_num_verts;

private:
	// Wing Edge Mesh data 
	std::vector<Vertex*> vertices;
	std::vector<Edge*> edges;
	std::vector<Face*> faces;

	// find the edge from edge list
	Edge* findEdge(STVector3 s, STVector3 e);
	Vertex* findVertex(STVector3 v);
	unsigned int insertVertex(STVector3 v);
};

#endif // CS148_SUBDIV_H_
